//
// Context.cpp
// Usage: Process run context env
// Create Date: 2009-03-19
// Author: MaJiuyue
//
#include <string.h>
#include "Context.h"
#include "SCI.h"

/* Marco used for make descriptor */
#define PREPARE_DESCRIPTOR(p_desc, base, limit, attr)			\
	((DESCRIPTOR *)p_desc)->limit_low = (limit) & 0xFFFF;		\
	((DESCRIPTOR *)p_desc)->base_low = (base) & 0xFFFF;		\
	((DESCRIPTOR *)p_desc)->base_mid = ((base)>>16) & 0xFFFF;	\
	((DESCRIPTOR *)p_desc)->attr1 = (attr) & 0xFF;			\
	((DESCRIPTOR *)p_desc)->limit_high_attr2 = (((limit)>>16) & 0xF) | ((attr)>>8) & 0xF0;								\
	((DESCRIPTOR *)p_desc)->base_high = ((base)>>24) & 0xFF;

#define PREPARE_CALLGATE(p_gate)				\
	((GATE *)p_gate)->offset_low = 0;			\
	((GATE *)p_gate)->selector = SELECTOR_LDT_USER_C;	\
	((GATE *)p_gate)->dcount = 0;				\
	((GATE *)p_gate)->attr = DA_386CGate | PRIV_KNL<<5;	\
	((GATE *)p_gate)->offset_high = 0;

Context::Context()
	: m_AddressSpace(0x40000000, 0x80000000)
{
	PREPARE_DESCRIPTOR(&ldts[0], 0x0, 0xBFFFF, DA_C   | DA_32 | DA_LIMIT_4K | SA_RPL3<<5)	
	PREPARE_DESCRIPTOR(&ldts[1], 0x0, 0xBFFFF, DA_DRW | DA_32 | DA_LIMIT_4K | SA_RPL3<<5);
	PREPARE_CALLGATE(&ldts[2]);
}

Context::Context(const Context & target)
	: m_PT(target.m_PT), m_AddressSpace(target.m_AddressSpace)
{
	memcpy(ldts, target.ldts, LDT_SIZE*sizeof(DESCRIPTOR));
}

Context::~Context()
{
}

void Context::SwitchContext()
{
	static u16 selector_ldt = SELECTOR_LDT;

	// Switch page table
	m_PT.SwitchTo();

	// Prepare LDT's GDT entry
	PREPARE_DESCRIPTOR(
		&GDT[INDEX_LDT],
		vir2lin((u32)ldts),
		LDT_SIZE*sizeof(DESCRIPTOR)-1,
		DA_LDT
	);
	// Switch LDT
	__asm__ __volatile__("lldt %0"::"m"(selector_ldt));
}

void Context::PrepareCallGate(addr_t entry, int param_cnt)
{
	GATE * CallGate = (GATE *)&ldts[LDT_INDEX_CALLGATE];
	
	if(param_cnt>32)
		panic("CallGate param count out of range: %d.", param_cnt);

	CallGate->offset_low = entry & 0xFFFF;
	CallGate->offset_high = entry >> 16;
	CallGate->dcount = param_cnt;
}

void * Context::MapPages(addr_t vaddr, addr_t paddr, int order)
{
	if(vaddr < 0xC0000000)
		m_PT.MapUserSpace(vaddr, paddr, order);
	else
		panic("In Context::MapPages() : map kernel pages not implement!");
	return (void *)vaddr;
}

void Context::UnmapPages(addr_t vaddr, int order)
{
	if(vaddr < 0xC0000000)
		m_PT.UnmapUserSpace(vaddr, order);
	else
		panic("In Context::UnmapPages() : unmap kernel pages not implement!");
}


void Context::ClearPageTable()
{
	m_PT.Clear();
}

